package com.github.gaojh.mvc.route;

import cn.hutool.core.util.StrUtil;
import com.github.gaojh.ioc.IocUtil;
import com.github.gaojh.ioc.annotation.Controller;
import com.github.gaojh.ioc.bean.ClassInfo;
import com.github.gaojh.ioc.bean.ClassScanner;
import com.github.gaojh.ioc.bean.Initializer;
import com.github.gaojh.mvc.annotation.*;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * @author gaojh
 * @since 2020/3/24
 */
@Slf4j
public class RouteInitializer implements Initializer {

    private static final Class<? extends Annotation>[] ANNO_CLASSES = new Class[]{GET.class, POST.class, OPTIONS.class};

    @Override
    public void init(ClassScanner classScanner) throws Exception {
        List<ClassInfo> classes = classScanner.getClassByAnnotation(Controller.class);
        if (classes.isEmpty()) {
            return;
        }
        for (ClassInfo classInfo : classes) {
            Class<?> clazz = classInfo.getBeanClass();
            Object object = IocUtil.getBean(clazz);

            RequestMapping controllerRequestMapping = clazz.getAnnotation(RequestMapping.class);
            String[] ctrlPath = controllerRequestMapping == null ? new String[]{"/"} : controllerRequestMapping.value();
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                for (Class<? extends Annotation> c : ANNO_CLASSES) {
                    if (method.isAnnotationPresent(c)) {
                        Annotation annotation = method.getAnnotation(c);
                        Method value = c.getMethod("value");
                        String methodPath = value.invoke(annotation).toString();
                        for (String path : ctrlPath) {
                            String url = path + methodPath;
                            if (!StrUtil.startWith(url, "/")) {
                                url = "/" + url;
                            }
                            url = StrUtil.replace(url, "//", "/");
                            RouteInfo routeInfo = new RouteInfo();
                            routeInfo.setType(clazz).setControllerObject(object).setMethod(method).setRequestPath(url);
                            routeInfo.setRequestMethod(new RequestMethod[]{RequestMethod.valueOf(c.getSimpleName())});
                            routeInfo.setParamNames(ParameterUtil.getMethodParamNames(method)).setParams(new Object[method.getParameterCount()]);
                            RouterUtil.addRouter(routeInfo);

                        }
                    }
                }


                if (method.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
                    String[] methodPath = methodRequestMapping.value().length == 0 ? new String[]{""} : methodRequestMapping.value();
                    for (String path : ctrlPath) {
                        for (String mpath : methodPath) {
                            String url = path + mpath;
                            if (!StrUtil.startWith(url, "/")) {
                                url = "/" + url;
                            }
                            url = StrUtil.replace(url, "//", "/");
                            RouteInfo routeInfo = new RouteInfo();
                            routeInfo.setType(clazz).setControllerObject(object).setMethod(method).setRequestPath(url);

                            if (methodRequestMapping.method().length == 0) {
                                routeInfo.setRequestMethod(Arrays.stream(RequestMethod.getRequestMethodAll()).map(RequestMethod::valueOf).toArray(RequestMethod[]::new));
                            } else {
                                routeInfo.setRequestMethod(methodRequestMapping.method());
                            }

                            routeInfo.setParamNames(ParameterUtil.getMethodParamNames(method)).setParams(new Object[method.getParameterCount()]);
                            RouterUtil.addRouter(routeInfo);
                        }
                    }
                }

            }
        }

    }
}
